declare module "vue/types/vue" {
  interface VueConstructor {
    [key: string]: any;
  }
}

declare global {
  interface Window {
    [key: string]: any;
  }
}

import {
  Callback, AnyCallback, AccountCallback, AsynCallback, CheckType, IModal, IAccount, IEngineLogic, IInputObj, IAsynHandler
} from "../bean";
import Vue from "vue";
import axios from "axios";
axios.defaults.timeout = 30000;

var _tag: string | null = null;

class EngineBase {
  _isDebugMode: boolean = false;
  _debugServer: number = 0;
  _platformInfo: string | null = null;
  _platformCallbackList: Array<typeof AnyCallback> = [];
  _storageMap: Map<string, any> = new Map();
  _jumpTime: number = 0;

  isDebugMode() {
    return this._isDebugMode;
  }

  log(tag: any, msg?: any): void {
    if (!this._isDebugMode || !tag) {
      return;
    }
    if (msg) {
      console.log(_tag + "__" + tag, msg);
    } else {
      console.log(_tag + "__log", tag);
    }
  }

  getCurrentPlatform(): string {
    return this._platformInfo;
  }

  _setCurrentPlatform(info: string): void {
    this._platformInfo = info;
    if (this._platformCallbackList.length > 0) {
      for (let i = 0; i < this._platformCallbackList.length; i++) {
        let callback = this._platformCallbackList[i];
        if (callback) {
          callback(info);
        }
      }
    }
  }

  registerPlatformCallback(callback: typeof AnyCallback): void {
    if (callback) {
      this._platformCallbackList.push(callback);
    }
  }

  unRegisterPlatformCallback(callback: typeof AnyCallback): void {
    if (callback) {
      let index = this._platformCallbackList.indexOf(callback);
      if (index > -1) {
        this._platformCallbackList.splice(index, 1);
      }
    }
  }

  _initStorage(): void {
    this._storageMap.clear();
    if (sessionStorage && sessionStorage.length > 0) {
      for (let i = 0; i < sessionStorage.length; i++) {
        let key = sessionStorage.key(i);
        let value = sessionStorage.getItem(key);
        this._storageMap.set(key, value);
      }
    }
  }

  _setStorage(key: string, value: any): void {
    this._storageMap.set(key, value);
    if (sessionStorage) {
      sessionStorage.setItem(key, value);
    }
  }

  _removeStorage(key: string): void {
    this._storageMap.delete(key);
    if (sessionStorage) {
      sessionStorage.removeItem(key);
    }
  }

  _clearStorage(): void {
    this._storageMap.clear();
    if (sessionStorage) {
      sessionStorage.clear();
    }
  }

  checkData(data: any, type: string | CheckType): boolean {
    if (data == undefined || data == null) {
      return false;
    }
    if (type) {
      let callback = checkMap.get(type);
      if (callback) {
        return callback(typeof data == "string" ? data.trim() : data + "");
      }
    }
    return true;
  }

  getFormatTime(data: any, format: string): string {
    if (data == undefined || data == null || !format) {
      return "";
    }
    let date = getDate(data);
    if (!date) {
      return "";
    }
    let result = format;
    if (result.indexOf("yyyy") > -1) {
      result = result.replace("yyyy", date.getFullYear() + "");
    }
    if (result.indexOf("MM") > -1) {
      let temp = date.getMonth() + 1;
      result = result.replace("MM", temp < 10 ? "0" + temp : "" + temp);
    }
    if (result.indexOf("dd") > -1) {
      let temp = date.getDate();
      result = result.replace("dd", temp < 10 ? "0" + temp : "" + temp);
    }
    if (result.indexOf("HH") > -1) {
      let temp = date.getHours();
      result = result.replace("HH", temp < 10 ? "0" + temp : "" + temp);
    }
    if (result.indexOf("mm") > -1) {
      let temp = date.getMinutes();
      result = result.replace("mm", temp < 10 ? "0" + temp : "" + temp);
    }
    if (result.indexOf("ss") > -1) {
      let temp = date.getSeconds();
      result = result.replace("ss", temp < 10 ? "0" + temp : "" + temp);
    }
    return result;
  }

  getFormatTimeByLen(data: any, format: string): string {
    if (data == undefined || data == null || !format) {
      return "";
    }
    let left = data;
    if (typeof left != "number") {
      left = Number(left);
      if (isNaN(left)) {
        return "";
      }
    }
    if (left < 0) {
      left = -left;
    }
    let result = format;
    if (result.indexOf("dd") > -1) {
      let temp = Math.floor(left / 86400);
      result = result.replace("dd", temp < 10 ? "0" + temp : "" + temp);
      left = left % 86400;
    }
    if (result.indexOf("HH") > -1) {
      let temp = Math.floor(left / 3600);
      result = result.replace("HH", temp < 10 ? "0" + temp : "" + temp);
      left = left % 3600;
    }
    if (result.indexOf("mm") > -1) {
      let temp = Math.floor(left / 60);
      result = result.replace("mm", temp < 10 ? "0" + temp : "" + temp);
      left = left % 60;
    }
    if (result.indexOf("ss") > -1) {
      result = result.replace("mm", left < 10 ? "0" + left : "" + left);
    }
    return result;
  }

  getTimeNumber(data: any): number {
    if (data == undefined || data == null) {
      return 0;
    }
    if (data instanceof Date) {
      let value = data.getTime();
      if (isNaN(value)) {
        return 0;
      }
      return value / 1000;
    }
    if (typeof data == "number") {
      return data;
    }
    if (typeof data == "string") {
      try {
        let value = Date.parse(setTimeString(data));
        if (isNaN(value)) {
          return 0;
        }
        return value / 1000;
      } catch (e) { }
    }
    return 0;
  }

  getDateByType(data: any, type?: string): Date | null {
    let result = getDate(data);
    if (!result) {
      return result;
    }
    if (type == "start") {
      result = getDate(result.toLocaleDateString());
    } else if (type == "end") {
      result = getDate(result.toLocaleDateString());
      if (result) {
        result = getDate(result.getTime() + 24 * 60 * 60 * 1000 - 1);
      }
    }
    return result;
  }

  toPrice(data: any, ratio?: any): number {
    if (data == undefined || data == null) {
      return 0;
    }
    let result = data;
    if (typeof result != "number") {
      result = Number(result);
      if (isNaN(result)) {
        return 0;
      }
    }
    let realRatio = ratio;
    if (realRatio == undefined || realRatio == null) {
      realRatio = 2;
    } else if (typeof realRatio != "number") {
      realRatio = Number(realRatio);
      if (isNaN(realRatio)) {
        realRatio = 2;
      }
    }
    realRatio = Math.pow(10, realRatio);
    return Math.round((result + 0.0000000003) * realRatio) / realRatio;
  }

  toFloorPrice(data: any, ratio?: any): number {
    if (data == undefined || data == null) {
      return 0;
    }
    let result = data;
    if (typeof result != "number") {
      result = Number(result);
      if (isNaN(result)) {
        return 0;
      }
    }
    let realRatio = ratio;
    if (realRatio == undefined || realRatio == null) {
      realRatio = 2;
    } else if (typeof realRatio != "number") {
      realRatio = Number(realRatio);
      if (isNaN(realRatio)) {
        realRatio = 2;
      }
    }
    realRatio = Math.pow(10, realRatio);
    return Math.floor((result + 0.0000000003) * realRatio) / realRatio;
  }

  getUrlSuffixObj(url: string | undefined | null): object | null {
    if (!url) {
      return null;
    }
    let realUrl = decodeURIComponent(url);
    if (!realUrl) {
      return null;
    }
    if (realUrl.endsWith("#/")) {
      realUrl = realUrl.substring(0, realUrl.length - 2);
    }
    let index = realUrl.indexOf("?");
    if (index < 0 || (index + 1) >= realUrl.length) {
      return null;
    }
    let str = realUrl.substring(index + 1, realUrl.length);
    let obj: any = null;
    let array = str.split("&");
    for (let i = 0; i < array.length; i++) {
      let item = array[i];
      let itemIndex = item.indexOf("=");
      if (itemIndex >= 0 && itemIndex < item.length) {
        let itemKey = item.substring(0, itemIndex);
        let itemValue = item.substring(itemIndex + 1, item.length);
        if (!obj) {
          obj = {};
        }
        obj[itemKey] = itemValue;
      }
    }
    return obj;
  }

  getHtmlStyleObj(style: string | undefined | null): object | null {
    if (!style) {
      return null;
    }
    let obj: any = null;
    style.trim().split(";").forEach(item => {
      let subArray = item.split(":");
      if (subArray.length == 2) {
        let key = subArray[0].trim();
        let realkey = "";
        let forceUp = false;
        for (let i = 0; i < key.length; i++) {
          let keyItem = key.charAt(i);
          if (keyItem == "-") {
            forceUp = true;
          } else {
            if (forceUp) {
              forceUp = false;
              realkey += keyItem.toUpperCase();
            } else {
              realkey += keyItem;
            }
          }
        }
        if (realkey.length > 0) {
          if (!obj) {
            obj = {};
          }
          obj[realkey] = subArray[1].trim();
          try {
            let result = JSON.parse(obj[realkey]);
            if (typeof result == "object") {
              obj[realkey] = result;
            }
          } catch (e) { }
        }
      }
    });
    return obj;
  }

  getUrlByParamObj(dest: string, param?: object | null): string {
    if (param) {
      let query = null;
      for (let key in param) {
        if (!query) {
          query = "?";
        } else {
          query += "&";
        }
        let value = param[key];
        if (value && typeof value == "object") {
          try {
            value = JSON.stringify(value);
          } catch (e) { }
        }
        query += key + "=" + value;
      }
      if (query) {
        dest += query;
      }
    }
    return dest;
  }

  createInputObj(obj?: any): IInputObj {
    return new InputObj(obj);
  }

  createAsynHandler(): IAsynHandler {
    return new AsynHandler();
  }

  toast(msg: any): void { }

  showLoading(msg?: string): void { }

  hideLoading(): void { }

  showModal(param: IModal, callback?: typeof Callback): void { }

  loadScript(src: string, callback: typeof Callback): void {
    for (let key in document.body.children) {
      let item = document.body.children[key];
      if (item && item.id == src) {
        if (callback) {
          callback(true, null);
        }
        return;
      }
    }
    let script = document.createElement("script");
    script.id = src;
    script.type = "text/javascript";
    script.src = src;
    script.addEventListener("load", () => {
      if (callback) {
        callback(true, null);
      }
    }, false);
    document.body.append(script);
  }

  setClipboardData(text: string, callback?: typeof Callback): void {
    if (window.clipboardData) {
      try {
        window.clipboardData.setData("Text", text);
        if (callback) {
          callback(true, null);
        }
      } catch (error) {
        if (callback) {
          callback(false, error);
        }
      }
    } else {
      try {
        let oInput = document.createElement("input");
        oInput.value = text + "";
        document.body.appendChild(oInput);
        oInput.select();
        document.execCommand("Copy");
        document.body.removeChild(oInput)
        if (callback) {
          callback(true, null);
        }
      } catch (error) {
        if (callback) {
          callback(false, error);
        }
      }
    }
  }

  getClipboardData(callback: typeof Callback): void {
    if (!callback) {
      return;
    }
    if (window.clipboardData) {
      try {
        let text = window.clipboardData.getData("Text");
        callback(true, text);
      } catch (error) {
        callback(false, error);
      }
    } else {
      callback(false, "getClipboard isn't support");
    }
  }

  getWindowWidth(): number {
    return document.body.clientWidth;
  }

  getWindowHeight(): number {
    return document.body.clientHeight;
  }

  setPageTitle(title: string): void {
    document.title = title;
  }

  finishPage(backPageNum?: number): void {
    Vue.instance.$router.go(backPageNum ? (backPageNum > 0 ? -backPageNum : backPageNum) : -1);
  }

  jumpPage(dest: string, param?: object | null, isFinish?: boolean): void {
    if (!dest) {
      return;
    }
    let option: any = {
      name: dest
    };
    if (param) {
      option.params = param;
      this.setAccountCache(dest, param);
    } else {
      this.setAccountCache(dest);
    }
    if (Vue.instance.$route.name == dest || new Date().getTime() - this._jumpTime < 20) {
      return;
    }
    this._jumpTime = new Date().getTime();
    if (isFinish) {
      Vue.instance.$router.replace(option);
    } else {
      Vue.instance.$router.push(option);
    }
  }

  getPageParam(): object | null {
    // let param = Vue.instance.$route.params;
    // if (param) {
    //   for (let key in param) {
    //     return param;
    //   }
    // }
    let name = Vue.instance.$route.name;
    return this.getAccountCache(name);
  }

  getAccountCache(key: string): any {
    if (!key) {
      return null;
    }
    let value = this._storageMap.get(getRealKey(key));
    if (value == undefined || value == null) {
      return null;
    }
    if (typeof value != "string") {
      return value;
    }
    value = decryptData(value);
    try {
      let result = JSON.parse(value);
      if (typeof result == "object") {
        return result;
      }
    } catch (e) { }
    return value;
  }

  setAccountCache(key: string, value?: any): void {
    if (!key) {
      return;
    }
    let realKey = getRealKey(key);
    if (value != undefined && value != null) {
      let result: string;
      if (typeof value != "string") {
        try {
          result = JSON.stringify(value);
        } catch (e) {
          result = value + "";
        }
      } else {
        result = value;
      }
      let realValue = encryptData(result);
      this._setStorage(realKey, realValue);
    } else {
      this._removeStorage(realKey);
    }
  }

  clearAccountCache(): void {
    this._clearStorage();
    if (this._platformCallbackList.length > 0) {
      this._platformCallbackList.splice(0, this._platformCallbackList.length);
    }
  }
}

function getRealKey(param: string): string {
  return "temp_" + param;
}

function encryptData(param: any): string {
  return param;
}

function decryptData(param: any): any {
  return param;
}

let checkMap = new Map();
checkMap.set("integer", function (value: string) { // 是否是正整数
  return /^[0-9]*$/.test(value);
});
checkMap.set("number", function (value: string) { // 是否是正数
  if (value.length < 1) {
    return false;
  }
  let result = Number(value);
  if (isNaN(result)) {
    return false;
  }
  return result >= 0;
});
checkMap.set("integerAll", function (value: string) { // 是否是整数
  return /^[0-9]*$/.test(value) || /^-[0-9]*$/.test(value);
});
checkMap.set("numberAll", function (value: string) { // 是否是数字
  if (value.length < 1) {
    return false;
  }
  return !isNaN(Number(value));
});
checkMap.set("phone", function (value: string) { // 是否是手机号码
  return /^1(3|4|5|6|7|8|9)\d{9}$/.test(value);
});
checkMap.set("carNumber", function (value: string) { // 是否是车牌号码
  return /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$/.test(value) ||
    /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9]{6}$/.test(value);
});
checkMap.set("idCard", function (value: string) { // 是否是身份证号码
  return /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(value);
});
checkMap.set("email", function (value: string) { // 是否是email
  return /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/.test(value);
});
checkMap.set("http", function (value: string) { // 是否以http开头
  return value.toLocaleLowerCase().startsWith("http://") || value.toLocaleLowerCase().startsWith("https://");
});
checkMap.set("https", function (value: string) { // 是否以https开头
  return value.toLocaleLowerCase().startsWith("https://");
});

function getDate(data: any): Date | null {
  if (data == undefined || data == null) {
    return new Date();
  }
  if (data instanceof Date) {
    if (!isNaN(data.getTime())) {
      return data;
    }
    return null;
  }
  if (typeof data == "number") {
    return new Date(data * 1000);
  }
  if (typeof data == "string") {
    let nData = Number(data);
    if (!isNaN(nData)) {
      return new Date(nData * 1000);
    }
    let date;
    try {
      date = new Date(data);
      if (!isNaN(date.getTime())) {
        return date;
      }
      date = null;
    } catch (e) {
      date = null;
    }
    let value = setTimeString(data);
    try {
      let array = value.split(" ");
      for (let i = 0; i < array.length; i++) {
        let item = array[i];
        if (item.indexOf("/") > -1) {
          date = setDateByDay(item, date);
        } else if (item.indexOf(":") > -1) {
          date = setDateByHour(item, date);
        }
      }
      return date;
    } catch (e) { }
  }
  return null;
}

function setTimeString(str: string): string {
  return str.replace(/-/g, "/").replace(/年/g, "/").replace(/月/g, "/").replace(/日/g, " ")
    .replace(/：/g, ":").replace(/小时/g, ":").replace(/分钟/g, ":").replace(/秒/g, " ")
    .replace(/点/g, ":").replace(/分/g, ":");
}

function setDateByDay(param: string, date: Date | null): Date | null {
  let array: any[] = param.split("/");
  if (array.length == 3) { // 判断成年、月、日
    if (checkNumberArray(array)) {
      let year, day;
      if (array[2] > array[0]) {
        year = array[2];
        day = array[0];
      } else {
        year = array[0];
        day = array[2];
      }
      if (array[1] >= 1 && array[1] <= 12 && day >= 1 && day <= 31) {
        if (!date) {
          date = new Date();
        }
        date.setUTCFullYear(year, array[1] - 1, day);
      }
    }
  } else if (array.length == 2) { // 判断成月和日
    if (checkNumberArray(array)) {
      if (array[0] >= 1 && array[0] <= 12 && array[1] >= 1 && array[1] <= 31) {
        if (!date) {
          date = new Date();
        }
        date.setUTCFullYear(date.getFullYear(), array[0] - 1, array[1]);
      }
    }
  }
  return date;
}

function setDateByHour(param: string, date: Date | null): Date | null {
  let array: any[] = param.split(":");
  if (array.length == 3) { // 判断成小时、分钟、秒
    if (checkNumberArray(array)) {
      if (array[0] >= 0 && array[0] <= 24 && array[1] >= 0 && array[1] <= 59 &&
        array[2] >= 0 && array[2] <= 59) {
        if (!date) {
          date = new Date();
        }
        date.setUTCHours(array[0] - 8, array[1], array[2]);
      }
    }
  } else if (array.length == 2) { // 判断成小时、分钟
    if (checkNumberArray(array)) {
      if (array[0] >= 0 && array[0] <= 24 && array[1] >= 0 && array[1] <= 59) {
        if (!date) {
          date = new Date();
        }
        date.setUTCHours(array[0] - 8, array[1], 0);
      }
    }
  }
  return date;
}

function checkNumberArray(array: any[]): boolean {
  for (let i = 0; i < array.length; i++) {
    let item = Number(array[i]);
    if (isNaN(item)) {
      return false;
    }
    array[i] = item;
  }
  return true;
}

class InputObj implements IInputObj {
  _obj: any = {};
  _visible = false;
  onChange: Function | null = null;

  constructor(obj: any) {
    this.setData(obj);
  }

  setData(obj: any): void {
    if (obj) {
      Object.assign(this, obj);
    }
  }

  getData(): object {
    let obj: any = {};
    Object.getOwnPropertyNames(this).forEach(key => {
      if (key != "__ob__" && key != "_obj" && key != "_visible" && key != "onChange") {
        let value = this[key];
        if (value != undefined) {
          obj[key] = value;
        }
      }
    });
    return obj;
  }

  clearData(): void {
    Object.getOwnPropertyNames(this).forEach(key => {
      if (key != "__ob__" && key != "_obj" && key != "_visible" && key != "onChange") {
        this[key] = undefined;
      }
    });
  }

  checkData(key?: string): boolean {
    if (key) {
      let checkCallback = this._obj[key];
      return !checkCallback || checkCallback();
    }
    for (let objKey in this._obj) {
      let checkCallback = this._obj[objKey];
      if (checkCallback && !checkCallback()) {
        return false;
      }
    }
    return true;
  }

  setDialogVisible(visible: boolean): void {
    this._visible = visible;
  }

  isDialogVisible(): boolean {
    return this._visible;
  }

  _register(key: string, checkCallback: Function) {
    this._obj[key] = checkCallback;
  }

  _unRegister(key: string) {
    delete this._obj[key];
  }

  _watch(key: string, callback: Function, formatCallback?: Function): void {
    let value = this[key];
    try {
      Object.defineProperty(this, key, {
        configurable: true,
        get: () => {
          return value;
        },
        set: (val) => {
          if (formatCallback) {
            value = formatCallback(val);
          } else {
            value = val;
          }
          callback(value, val);
          if (this.onChange) {
            this.onChange(key, value);
          }
        }
      });
    } catch (e) {
      console.log(_tag + "__watch", e);
    }
    this[key] = value;
  }

  [propName: string]: any;
}

class AsynHandler implements IAsynHandler {
  _data: any = null;
  _listenList: (typeof AsynCallback)[] | null = null;

  register(key: string): typeof Callback {
    if (!this._data) {
      this._data = {};
    }
    if (Object.keys(this._data).indexOf(key) == -1) {
      this._data[key] = undefined;
    }
    let self = this;
    return function (success, data) {
      self._data[key] = (success && data !== undefined) ? data : null;
      if (self.checkData()) {
        if (self._listenList) {
          self._listenList.forEach(callback => {
            callback(self._data);
          });
          self._listenList = null;
        }
      }
    };
  }

  getData(callback: typeof AsynCallback): void {
    if (!callback) {
      return;
    }
    if (this.checkData()) {
      callback(this._data);
    } else {
      if (!this._listenList) {
        this._listenList = [];
      }
      this._listenList.push(callback);
    }
  }

  checkData(): boolean {
    if (!this._data) {
      return false;
    }
    for (let key in this._data) {
      if (this._data[key] === undefined) {
        return false;
      }
    }
    return true;
  }
}

const KEY_SERVERMODE = "eg-a";
const KEY_CODE = "eg-b";
const KEY_TOKEN = "eg-c";
const KEY_REFRESHTOKEN = "eg-d";
const KEY_EXPIREDATE = "eg-e";
const KEY_NICKNAME = "eg-f";
const KEY_AVATARURL = "eg-g";
const KEY_PHONE = "eg-h";

export default class Engine extends EngineBase {
  logic: IEngineLogic = null;
  _mapKey: string | null = null;
  _tokenUrl: string | null = null;
  _userCode = null;
  _userAuthorization = null;
  _tokenTimeout = null;

  init(logic: IEngineLogic, tag: string, isDebugMode: boolean, debugServer: number, mapKey: string, tokenUrl: string): void {
    this.logic = logic;
    _tag = tag;
    this._isDebugMode = isDebugMode;
    this._debugServer = debugServer;
    this._mapKey = mapKey;
    this._tokenUrl = tokenUrl;
  }

  getMapKey(): string {
    return this._mapKey;
  }

  clearAccountCache(): void {
    this._userCode = null;
    this._userAuthorization = null;
    if (this._tokenTimeout) {
      clearTimeout(this._tokenTimeout);
      this._tokenTimeout = null;
    }
    this.logic.clearLogicCache();
    super.clearAccountCache();
  }

  _initStorage(): void {
    super._initStorage();
    this.checkEngine();
  }

  checkEngine(): void {
    let isReset: boolean = false;
    let serveMode = this.getAccountCache(KEY_SERVERMODE);
    if (serveMode) {
      if (this._debugServer != serveMode) {
        this.clearAccountCache();
        isReset = true;
      }
    } else {
      if (this.getAccountCache(KEY_CODE)) {
        this.clearAccountCache();
        isReset = true;
      }
    }
    this.logic.checkEngine(isReset);
    if (!isReset) {
      this._judgeRefreshToken();
    }
  }

  getUrl(isShowLoading: boolean, url: string, param?: object | null, withoutAccount?: boolean, timeout?: number, linkId?: string): Promise<any> {
    return this.requestUrl(isShowLoading, url, "GET", param, withoutAccount, timeout, linkId);
  }

  postUrl(isShowLoading: boolean, url: string, param?: object | null, withoutAccount?: boolean, timeout?: number, linkId?: string): Promise<any> {
    return this.requestUrl(isShowLoading, url, "POST", param, withoutAccount, timeout, linkId);
  }

  deleteUrl(isShowLoading: boolean, url: string, param?: object | null, withoutAccount?: boolean, timeout?: number, linkId?: string): Promise<any> {
    return this.requestUrl(isShowLoading, url, "DELETE", param, withoutAccount, timeout, linkId);
  }

  putUrl(isShowLoading: boolean, url: string, param?: object | null, withoutAccount?: boolean, timeout?: number, linkId?: string): Promise<any> {
    return this.requestUrl(isShowLoading, url, "PUT", param, withoutAccount, timeout, linkId);
  }

  // "OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT"
  requestUrl(isShowLoading: boolean, url: string, method: string, param: object | null, withoutAccount: boolean, timeout: number, linkId: string): Promise<any> {
    return new Promise((resolve, reject) => {
      if (!url || !method) {
        reject("param error");
        return;
      }
      if (navigator && !navigator.onLine) {
        reject("请连接网络");
        return;
      }
      if (withoutAccount) {
        if (isShowLoading) {
          this.showLoading();
        }
        this.requestHttpUrl(url, method, param, this.logic.getRequestHeader(linkId), timeout).then((res: any) => {
          this._requestSuccess(res, isShowLoading, url, param, resolve, reject);
        }).catch((reason: any) => {
          this._requestFail(reason, isShowLoading, url, param, reject);
        });
      } else {
        this.getAccount((data: IAccount | null) => {
          if (!data) {
            reject("登录失败，无法获取数据");
            return;
          }
          if (isShowLoading) {
            this.showLoading();
          }
          this.requestHttpUrl(url, method, param, this.logic.getRequestHeader(linkId), timeout).then((res: any) => {
            this._requestSuccess(res, isShowLoading, url, param, resolve, reject);
          }).catch((reason: any) => {
            this._requestFail(reason, isShowLoading, url, param, reject);
          });
        });
      }
    });
  }

  uploadFile(isShowLoading: boolean, url: string, fileName: string, filePath: string, formData: FormData): Promise<any> {
    if (!fileName && !filePath) {
      return this.requestUrl(isShowLoading, url, "POST", formData, null, null, null);
    }
    return new Promise((resolve, reject) => {
      if (!url) {
        reject("param error");
        return;
      }
      if (navigator && !navigator.onLine) {
        reject("请连接网络");
        return;
      }
      if (isShowLoading) {
        this.showLoading();
      }
      this.requestUploadFile(url, fileName, filePath, formData, this.logic.getUploadFileHeader()).then((res: any) => {
        this._requestSuccess(res, isShowLoading, url, fileName + "_" + filePath, resolve, reject);
      }).catch((reason: any) => {
        this._requestFail(reason, isShowLoading, url, fileName + "_" + filePath, reject);
      });
    });
  }

  requestHttpUrl(url: string, method: string, param: any, header: object, timeout: number): Promise<any> {
    let option: any = {};
    option.url = url;
    option.method = method;
    if (param && method == "GET") {
      let query = null;
      for (let key in param) {
        let value = param[key];
        if (value && typeof value != "string") {
          try {
            value = JSON.stringify(value);
          } catch (error) { }
        }
        if (!query) {
          query = "?";
        } else {
          query += "&";
        }
        query += key + "=" + value;
      }
      if (query) {
        option.url += query;
      }
    } else {
      option.data = param;
    }
    option.headers = header;
    if (timeout) {
      option.timeout = timeout;
    }
    // axios.interceptors.request.use(config=> {
    // 	// Do something before request is sent
    // 	return config;
    // }, error=> {
    // 	// Do something with request error
    // 	return Promise.reject(error);
    // });
    return axios(option);
  }

  requestUploadFile(url: string, fileName: any, filePath: string, formData: FormData, header: object) {
    let option: any = {};
    option.url = url;
    option.name = fileName;
    option.filePath = filePath;
    if (formData) {
      option.formData = formData;
    }
    option.headers = header;
    return axios(option);
  }

  _requestFail(reason: object, isShowLoading: boolean, url: string, param: object | string, reject: Function): void {
    if (isShowLoading) {
      this.hideLoading();
    }
    this.log("requestUrl:", url);
    this.log("reason:", reason);
    this.log("requestParam:", param);
    this.toast("获取数据出错");
    reject("获取数据出错");
  }

  _requestSuccess(res: any, isShowLoading: boolean, url: string, param: object | string, resolve: Function, reject: Function): void {
    if (isShowLoading) {
      this.hideLoading();
    }
    if (!res || !res.data) {
      this.log("requestUrl:", url + "\n res is null");
      this.log("requestParam:", param);
      this.toast("获取数据有错");
      reject("获取数据有错");
      return;
    }
    if (typeof res.data == "string") {
      try {
        res.data = JSON.parse(res.data);
      } catch (e) {
        this.log("requestUrl:", url);
        this.log("parse:", res.data);
        this.log("requestParam:", param);
        this.toast("获取数据解析有错");
        reject("获取数据解析有错");
        return;
      }
    }
    res = res.data;
    if (res.status == 502) {
      this.toast("服务器升级中");
      reject("服务器升级中");
      return;
    }
    if (res.status != 200) {
      this.log("requestUrl:", url + "\n status:" + res.status + " statusMsg:" + res.statusMsg);
      this.log("requestParam:", param);
      reject(res.statusMsg);
      return;
    }
    let resData = res.res;
    if (!resData) {
      this.log("requestUrl:", url);
      this.log("res:", res);
      this.log("requestParam:", param);
      reject("请求发送失败");
      return;
    }
    let code = resData.code;
    if (code == 1000) {
      resolve(resData.data);
    } else if (code == 500 || code == 2607) { // 过期
      this.log("requestUrl:", url + "\n code:" + code + " codeMsg:" + resData.codeMsg);
      this.log("requestParam:", param);
      reject("重新登录中");
      this.logic.handleRequestOverdue();
    } else if (code == 1026) {
      this.log("requestUrl:", url + "\n code:" + code + " codeMsg:" + resData.codeMsg);
      this.log("requestParam:", param);
      reject("设备授权已过期");
      this.logic.handleRequestOverdue();
    } else if (code == 1025) {
      this.log("requestUrl:", url + "\n code:" + code + " codeMsg:" + resData.codeMsg);
      this.log("requestParam:", param);
      reject("设备未授权");
      this.logic.handleRequestOverdue();
    } else {
      this.log("requestUrl:", url + "\n code:" + code + " codeMsg:" + resData.codeMsg);
      this.log("requestParam:", param);
      reject(resData.codeMsg);
    }
  }

  getAccount(callback: typeof AccountCallback): void {
    if (!this._userCode || !this._userAuthorization) {
      this._userCode = this.getAccountCache(KEY_CODE);
      this._userAuthorization = this.getAccountCache(KEY_TOKEN);
      if (!this._userCode || !this._userAuthorization) {
        this.logic.handleRequestLogin(callback);
        return;
      }
    }
    if (callback) {
      callback({
        code: this._userCode,
        token: this._userAuthorization
      });
    }
  }

  getAccountCodeSync(): string {
    if (this._userCode) {
      return this._userCode;
    }
    this._userCode = this.getAccountCache(KEY_CODE);
    return this._userCode;
  }

  getAccountTokenSync(): string {
    if (this._userAuthorization) {
      return this._userAuthorization;
    }
    this._userAuthorization = this.getAccountCache(KEY_TOKEN);
    return this._userAuthorization;
  }

  getMyNameSync(): string {
    return this.getAccountCache(KEY_NICKNAME);
  }

  getMyIconSync(): string {
    return this.getAccountCache(KEY_AVATARURL);
  }

  getMyPhoneSync(): string {
    return this.getAccountCache(KEY_PHONE);
  }

  saveAccount(useId: string, token: string, refreshToken: string, expireDate: string,
    nickName: string, avatarUrl: string, phone: string): void {
    this.setAccountCache(KEY_SERVERMODE, this._debugServer);
    this._userCode = useId;
    this._userAuthorization = token;
    this.setAccountCache(KEY_CODE, useId);
    this.setAccountCache(KEY_TOKEN, token);
    this.setAccountCache(KEY_REFRESHTOKEN, refreshToken);
    this.setAccountCache(KEY_EXPIREDATE, expireDate);
    this.setAccountCache(KEY_NICKNAME, nickName);
    this.setAccountCache(KEY_AVATARURL, avatarUrl);
    this.setAccountCache(KEY_PHONE, phone);
    this._judgeRefreshToken();
  }

  _judgeRefreshToken() {
    let expireDate = this.getAccountCache(KEY_EXPIREDATE);
    if (!expireDate) {
      return;
    }
    let timeLen = expireDate - new Date().getTime();
    if (isNaN(timeLen) || timeLen <= 0) { // 已过期，不处理
      return;
    }
    let limitLen = timeLen - 6 * 60 * 60 * 1000; // 大于6小时
    if (timeLen > 0) {
      if (this._tokenTimeout) {
        clearTimeout(this._tokenTimeout);
        this._tokenTimeout = null;
      }
      this._tokenTimeout = setTimeout(() => {
        this._getNewToken();
      }, limitLen);
    } else {
      this._getNewToken();
    }
  }

  _getNewToken() {
    if (!this._tokenUrl) {
      return;
    }
    let refreshToken = this.getAccountCache(KEY_REFRESHTOKEN);
    if (!refreshToken) {
      return;
    }
    this.postUrl(false, this._tokenUrl, { refreshToken: refreshToken }).then(data => {
      if (data) {
        this._userAuthorization = data.token;
        this.setAccountCache(KEY_TOKEN, data.token);
        this.setAccountCache(KEY_REFRESHTOKEN, data.refreshToken);
        this.setAccountCache(KEY_EXPIREDATE, data.expireDate);
      } else {
        this.log("refreshToken fail");
      }
      if (this._tokenTimeout) {
        clearTimeout(this._tokenTimeout);
        this._tokenTimeout = null;
      }
      this._tokenTimeout = setTimeout(() => {
        this._judgeRefreshToken();
      }, 600000);
    }).catch(reason => {
      this.log(reason);
      if (this._tokenTimeout) {
        clearTimeout(this._tokenTimeout);
        this._tokenTimeout = null;
      }
      this._tokenTimeout = setTimeout(() => {
        this._judgeRefreshToken();
      }, 600000);
    });
  }
}